/*-----------------------------------------------------------------------------
	The contents of this file are subject to the Mozilla Public License
	Version 1.1 (the "License"); you may not use this file except in
	compliance with the License.You may obtain a copy of the License at
	http://www.mozilla.org/MPL/MPL-1.1.html
	
	Software distributed under the License is distributed on an "AS IS"
	basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
	License for the specific language governing rights and limitations
	under the License.
	
	The Original Code is Fever Framework code.
	
	The Initial Developer of the Original Code is Romain Ecarnot.
	Portions created by Initial Developer are Copyright (C) 2006
	the Initial Developer. All Rights Reserved.
	
	Contributor(s):
	
	Unless required by applicable law or agreed to in writing, software
	distributed under the License is distributed on an "AS IS" BASIS,
	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
	See the License for the specific language governing permissions and
	limitations under the License.
-----------------------------------------------------------------------------*/
 
import fever.data.validators.StringValidator;
import fever.events.ValidationResultEvent;
import fever.log.FeverDebug;
import fever.utils.ArrayUtil;
import fever.utils.MathUtils;

/**
 * Validates password syntax security.
 * 
 * @see fever.data.validators.StringValidator
 * @see fever.data.validators.Validator
 * 
 * @author Romain Ecarnot
 */
class fever.data.validators.PasswordValidator extends StringValidator
{
	//-------------------------------------------------------------------------
	// Private properties
	//-------------------------------------------------------------------------
	
	private var _level : Number;
	
		
	//-------------------------------------------------------------------------
	// Public properties
	//-------------------------------------------------------------------------
	
	/** Password must contains number. */
	public var useNumber : Boolean;
	
	/** Password must mix lower and uppercase chars. */
	public var mixCaps : Boolean;
	
	/** Password must contains special chars. */
	public var useSpecial : Boolean;
	
	/** Indicates password level ( after validation ). */
	public function get level() : Number { return _level; }
	
	
	//-------------------------------------------------------------------------
	// Public API
	//-------------------------------------------------------------------------
	
	/**
	 * Constructor.
	 */
	public function PasswordValidator( )
	{
		super();
		
		useNumber = true;
		mixCaps = true;
		useSpecial = true;
		
		_level = -1;
	}
	
	/**
	 * Returns security level ( percent value ).
	 * 
	 * @return security level percent
	 */
	public function getPercentLevel(  ) : Number
	{
		if( _level > -1 )
		{
			var max : Number = MathUtils.getVariance( [ 100,0,0,15 ] );
			
			return 100 - MathUtils.percent( _level, max, true );
		}
		return -1;
	}
	
	
	//-------------------------------------------------------------------------
	// Private implementation
	//-------------------------------------------------------------------------
	
	private function _doValidation( object ) : ValidationResultEvent
	{
		var toValid : String = _buildValidationTarget( object );
		
		if( !_testRequired( toValid ) )
		{	
			_fireEventType( ValidationResultEvent.INVALID );
			return _event;
		}
		
		if( !_testMinLength( toValid ) )
		{	
			_fireEventType( ValidationResultEvent.INVALID );
			return _event;
		}
		
		if( !_testMaxLength( toValid ) )
		{	
			_fireEventType( ValidationResultEvent.INVALID );
			return _event;
		}
		
		if( !_checkPassword( toValid ) )
		{	
			_fireEventType( ValidationResultEvent.INVALID );
			return _event;
		}
		
		_prepareEvent( ValidationResultEvent.VALID, toValid, '');
		_fireEventType( ValidationResultEvent.VALID );
		
		return _event;
	}
	
	private function _checkPassword( 
		pass : String, 
		useNumber : Boolean, 
		useCaps : Boolean,
		useSpecial : Boolean ) : Boolean 
	{
		var nL : Number = pass.length;
		var nNum : Number = 0;
		var nUpper : Number = 0;
		var nLower : Number = 0;
		var nSpe : Number = 0;
		
		for( var i : Number = 0; i < nL; i++ )
		{
			var s : String = pass.charAt( i );
			var c : Number = pass.charCodeAt( i );
			
			if( ArrayUtil.contains( s, _resources.password_special_chars ) ) nSpe++;
			else if( c >= 97 && c <= 122 ) nLower++;
			else if( c >= 65 && c <= 90 ) nUpper++;
			else if( c >= 48 && c <= 57 ) nNum++;
		}
		
		var nPN : Number = MathUtils.percent( nNum, nL, true );		var nPL : Number = MathUtils.percent( nLower, nL, true );		var nPU : Number = MathUtils.percent( nUpper, nL, true );		var nPS : Number = MathUtils.percent( nSpe, nL, true );
		var nPLength : Number = ( ( nL / (  maxLength - minLength ) ) -1 ) * 100;
		nPLength = ( nPLength >= 50 ) ? 25 : 15;
		
		//TODO include or not password length in variance calculation
		_level = Math.round( MathUtils.getVariance( [ nPN, nPL, nPU, nPS, nPLength ] ) );
		
		FeverDebug.INFO( 'Level -> [ number : ' + nPN + ' ] [ lower : ' + nPL + ' ] [ upper : ' + nPU + ' ] [ special ' + nPS + ' ] [ length : ' + nPLength + ' ]' );		
		_event.message = '';
		
		if( useNumber && nNum == 0 ) _addTips( _resources.password_number );
		
		if( mixCaps && ( nLower == 0 || nUpper == 0 ) )
		{
			if( nLower == 0 ) _addTips( _resources.password_lower );
			if( nUpper == 0 ) _addTips(  _resources.password_upper );
		}
		
		if( useSpecial && nSpe == 0 ) _addTips( _resources.password_special );
		
		if( _event.message.length > 0 )
		{
			_prepareEvent( ValidationResultEvent.INVALID, pass, _resources.password_tips + ' ' + _event.message );
			
			return false;
		}
		else return true;
	}
	
	private function _addTips( tips : String ) : Void 
	{
		_event.message += ( ' ' + tips );
	}
}